<template>
  <div class="zeus-table-wrapper" ref="wrapper">
    <div
      :style="{ height: height + 'px', overflow: 'auto' }"
      ref="tableWrapper"
    >
      <table
        class="zeus-table"
        ref="table"
        :class="{ bordered, compact, striped }"
      >
        <thead>
          <tr>
            <!-- 展开头 -->
            <th
              v-if="expendProp"
              :style="{ width: '50px' }"
              class="zeus-table-center"
            ></th>
            <!-- CheckBox -->
            <th
              v-if="checkable"
              :style="{ width: '50px' }"
              class="zeus-table-center"
            >
              <input
                type="checkbox"
                ref="allChecked"
                :checked="areAllItemsSelected"
                @change="onChangeAllItems"
              />
            </th>
            <!-- 序号 -->
            <th
              :style="{ width: '50px' }"
              class="zeus-table-center"
              v-if="numberVisible"
            ></th>
            <!-- title -->
            <th
              v-for="item in columns"
              :key="item.prop"
              :style="{ width: item.width + 'px' }"
            >
              <div class="zeus-table-header">
                {{ item.title }}
                <span
                  v-if="item.prop in orderBy"
                  class="zeus-table-sorter"
                  @click="changeOrderBy(item.prop)"
                >
                  <Icon
                    :class="{ active: orderBy[item.prop] === 'asc' }"
                    name="asc"
                  ></Icon>
                  <Icon
                    :class="{ active: orderBy[item.prop] === 'desc' }"
                    name="desc"
                  ></Icon>
                </span>
              </div>
            </th>
            <!-- 用户自定义操作頭 -->
            <th
              v-if="$slots.default"
              class="zeus-table-center"
              ref="actionsHeader"
            >
              操作
            </th>
          </tr>
        </thead>
        <tbody>
          <template v-for="(item, index) in dataSource" :key="item.id">
            <tr>
              <!-- 展开列表icon -->
              <td
                :style="{ width: '50px' }"
                class="zeus-table-center"
                v-if="expendProp"
                @click="expendItem(item.id)"
              >
                <Icon
                  name="right"
                  :class="{
                    expend: inExpendIds(item.id),
                    NoExpend: !inExpendIds(item.id),
                  }"
                />
              </td>
              <!-- CheckBox -->
              <td
                v-if="checkable"
                :style="{ width: '50px' }"
                class="zeus-table-center"
              >
                <input
                  type="checkbox"
                  :checked="
                    selectedItems.filter((i) => i.id === item.id).length > 0
                  "
                  @change="onChangeItem(item, index, $event)"
                />
              </td>
              <!-- 序号 -->
              <td
                :style="{ width: '50px' }"
                class="zeus-table-center"
                v-if="numberVisible"
              >
                {{ index + 1 }}
              </td>
              <!-- 内容 -->
              <template v-for="column in columns" :key="column.prop">
                <td :style="{ width: column.width + 'px' }">
                  {{ item[column.prop] }}
                </td>
              </template>
              <!-- 用户自定义操作列 -->
              <td v-if="$slots.default">
                <div ref="actions" style="display: inline-block">
                  <slot :item="item" />
                </div>
              </td>
            </tr>
            <!-- 展开列内容 -->
            <tr
              v-if="inExpendIds(item.id)"
              :class="{ trExpend: inExpendIds(item.id) }"
            >
              <td :style="{ width: '50px' }"></td>
              <td :colspan="columns.length + expendedCellColSpan">
                {{ item[expendProp] || `&nbsp;` }}
              </td>
            </tr>
          </template>
        </tbody>
      </table>
    </div>
    <div v-if="loading" class="zeus-table-loading">
      <Icon name="loading" />
    </div>
  </div>
</template>

<script lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
export default {
  props: {
    dataSource: {
      type: Array,
      required: true,
      validator(array) {
        return !(array.filter((item) => item.id === undefined).length > 0)
      },
    },
    columns: {
      type: Array,
      required: true,
    },
    selectedItems: {
      type: Array,
      default: () => [],
    },
    numberVisible: {
      type: Boolean,
      default: false,
    },
    bordered: {
      type: Boolean,
      default: false,
    },
    compact: {
      type: Boolean,
      default: false,
    },
    striped: {
      type: Boolean,
      default: true,
    },
    orderBy: {
      type: Object,
      default: () => ({}),
    },
    loading: {
      type: Boolean,
      default: false,
    },
    height: {
      type: Number,
    },
    expendProp: {
      type: String,
    },
    checkable: {
      type: Boolean,
      default: false,
    },
  },
  setup(props, context) {
    let allChecked = ref<HTMLInputElement>(null)
    let table = ref<HTMLTableElement>(null)
    let wrapper = ref<HTMLDIivlement>(null)
    let tableWrapper = ref<HTMLDivElement>(null)
    let actions = ref<HTMLDivElement>(null)
    let actionsHeader = ref<HTMLDivElement>(null)

    let expendedIds = ref([])

    // tbody 里的CheckBox选择事件
    const onChangeItem = (item, index, e) => {
      let selected = e.target.checked
      let copy = JSON.parse(JSON.stringify(props.selectedItems))
      if (selected) {
        copy.push(item)
      } else {
        copy = copy.filter((i) => i.id !== item.id)
      }
      context.emit('update:selectedItems', copy)
    }
    // theader 里的CheckBox选择事件
    const onChangeAllItems = (e) => {
      let selected = e.target.checked
      context.emit('update:selectedItems', selected ? props.dataSource : [])
    }

    // 判断用户是否全选
    const areAllItemsSelected = computed(() => {
      let sources = props.dataSource.map((item) => item.id).sort()
      let selecteds = props.selectedItems.map((item) => item.id).sort()
      if (sources.length !== selecteds.length) {
        return false
      }
      let equal = true
      for (let i = 0; i < sources.length; i++)
        if (sources[i] !== selecteds[i]) {
          equal = false
          break
        }
      return equal
    })
    // 排序
    const changeOrderBy = (key) => {
      let copy = JSON.parse(JSON.stringify(props.orderBy))
      let oldValue = copy[key]
      if (oldValue === 'asc') {
        copy[key] = 'desc'
      } else if (oldValue === 'desc') {
        copy[key] = true
      } else {
        copy[key] = 'asc'
      }
      context.emit('update:orderBy', copy)
    }
    // 用户选择了item且不为全选时全选CheckBox状态为 -
    watch(
      () => props.selectedItems,
      (newValue, oldValue) => {
        if (props.selectedItems.length === props.dataSource.length) {
          allChecked.value.indeterminate = false
        } else if (props.selectedItems.length === 0) {
          allChecked.value.indeterminate = false
        } else {
          allChecked.value.indeterminate = true
        }
      }
    )

    onMounted(() => {
      // 固定表头 columns需要传入width
      let tableCopy = table.value.cloneNode(false)
      let tHead = table.value.childNodes[0]
      let { height } = tHead.getBoundingClientRect()
      tableCopy.classList.add('zeus-table-copy')
      tableCopy.appendChild(tHead)
      tableWrapper.value.style.marginTop = height + 'px'
      tableWrapper.value.style.height = props.height - height + 'px'
      wrapper.value.appendChild(tableCopy)

      // 设置用户自定义操作列和操作头宽度
      if (context.slots.default) {
        let div = actions.value
        let { width } = div.getBoundingClientRect()
        let parent = div.parentNode
        let styles = getComputedStyle(parent)
        let paddingLeft = styles.getPropertyValue('padding-left')
        let paddingRight = styles.getPropertyValue('padding-right')
        let borderLeft = styles.getPropertyValue('border-left-width')
        let borderRight = styles.getPropertyValue('border-right-width')
        let width2 =
          width +
          parseInt(paddingLeft) +
          parseInt(paddingRight) +
          parseInt(borderLeft) +
          parseInt(borderRight) +
          'px'
        if (tableWrapper.value.offsetWidth > tableWrapper.value.clientWidth) {
          let width3 =
            tableWrapper.value.offsetWidth - tableWrapper.value.clientWidth
          console.log(width3)

          actionsHeader.value.style.width = parseInt(width2) + width3 + 'px'
          parent.style.width = width2
        } else {
          actionsHeader.value.style.width = width2
          parent.style.width = width2
        }
      }
    })

    // 用户点击展开标签
    const expendItem = (id) => {
      if (inExpendIds(id)) {
        expendedIds.value.splice(expendedIds.value.indexOf(id), 1)
      } else {
        expendedIds.value.push(id)
      }
    }
    // 判断是否可展开
    const inExpendIds = (id) => {
      return expendedIds.value.indexOf(id) >= 0
    }

    // 计算展开列要跨越多少列
    const expendedCellColSpan = computed(() => {
      let result = 0
      if (props.numberVisible) {
        result += 1
      }
      if (props.checkable) {
        result += 1
      }
      if (props.expendProp) {
        result += 1
      }
      return result
    })

    return {
      allChecked,
      areAllItemsSelected,
      table,
      wrapper,
      tableWrapper,
      actions,
      actionsHeader,
      onChangeItem,
      onChangeAllItems,
      changeOrderBy,
      inExpendIds,
      expendItem,
      expendedCellColSpan,
    }
  },
}
</script>

<style lang="scss">
@import '../../styles/_var.scss';
$grey: darken($grey, 3%);
.zeus-table-wrapper {
  position: relative;
  overflow: auto;
  .zeus-table {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
    border-bottom: 1px solid $grey;

    &.bordered {
      border: 1px solid $grey;
      td,
      th {
        border: 1px solid $grey;
      }
    }
    &.compact {
      td,
      th {
        padding: 4px;
      }
    }
    &.striped {
      tbody {
        tr {
          &:nth-child(odd) {
            background: white;
          }
          &:nth-child(even) {
            background: lighten($grey, 4%);
          }
        }
      }
    }
    td,
    th {
      border-bottom: 1px solid $grey;
      padding: 8px;
      text-align: left;
      &.zeus-table-center {
        text-align: center;

        vertical-align: middle;
      }
    }
    &-header {
      display: flex;
      align-items: center;
    }
    &-sorter {
      display: inline-flex;
      flex-direction: column;
      margin: 0 4px;
      cursor: pointer;
      svg {
        width: 14px;
        height: 14px;
        fill: $grey;
        &:nth-child(1) {
          position: relative;
          bottom: -2px;
        }
        &:nth-child(2) {
          position: relative;
          top: -4px;
        }
        &.active {
          fill: rgb(24, 24, 24, 0.8);
        }
      }
    }
    &-loading {
      background: rgba(255, 255, 255, 0.8);
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      svg {
        width: 20px;
        height: 20px;
        @include spin;
      }
    }
    &-copy {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      background: white;
    }
    .expend {
      transition: all 0.3s;
      transform: rotate(90deg);
    }
    .NoExpend {
      transition: all 0.3s;
    }
  }
}
</style>
